#!/usr/bin/env python
#coding=utf-8
##############################################################################
#
#    OSS File Tools, Open Source Management Solution
#    Copyright (c) 2013, VnSoft (vnsoft.he@gmail.com)
#    All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
##############################################################################

from oss.oss_api import *
from oss.oss_util import *
from oss.oss_xml_handler import *
import datetime,time
import os, sys, re, time,stat,platform
import string
import gtk

HOST="oss.aliyuncs.com"
ACCESS_ID=""
SECRET_ACCESS_KEY=""
OSSFT_VERSION='V0.1'
HEADER_KEYS=['content-language' ,'expires','cache-control' ,'content-encoding','content-disposition']
def format_datetime(osstimestamp):
    date = re.compile("(\.\d*)?Z").sub(".000Z", osstimestamp)
    ts = time.strptime(date, "%Y-%m-%dT%H:%M:%S.000Z")
    return time.strftime("%Y-%m-%d %H:%M", ts)

#转换本地文件的时间格式
def format_LocalModifyTime(localtime):
    return time.strftime("%Y-%m-%d %H:%M",time.strptime(time.ctime(localtime)))

#转换为utf8编码字串
def format_utf8(string):
    string = smart_code(string)
    if isinstance(string, unicode):
        string = string.encode('utf-8')                
    return string

#转换为本地编码字串
def format_local(string):
    string = smart_code(string)
    if isinstance(string,unicode):    
        try:
            tmp = string.encode('ascii')
        except:
            try:
                tmp = string.encode('gbk')
            except:
                try:
                    tmp = string.encode('big5')
                except:
                    try:
                        tmp = string.encode('utf-8')
                    except:
                        tmp = string
    else:
        tmp = string
    return tmp
    
def getDrives():
    dlist=[]
    if platform.system()=='Windows':
        dl = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        for d in ['%s:\\' % d for d in dl if os.path.exists('%s:' % d)]:
            dlist.append(d)
    else:
        dlist.append('/')    
    return dlist
    
def parse_bucket_object(path):
    pather = path.split('/')
    bucket = ""
    object = ""
    if len(pather) > 0:
        bucket= pather[0]
    if len(pather) > 1:
        object += '/'.join(pather[1:])
    object = smart_code(object)
    return (bucket, object)

def get_oss(show_bar = False):
    oss = OssAPI(HOST, ACCESS_ID, SECRET_ACCESS_KEY)
    oss.show_bar = show_bar 
    return oss

def cmd_getallbucket():
    try:
        res = get_oss().get_service()
    except:
        return None
    return res

def get_ossFileSize(bucket,object):
    res = get_oss().get_object(bucket, object)
    
    if res.status == 200:
        header = {}
        header = convert_header2map(res.getheaders())
        filesize = safe_get_element("content-length", header)
        return filesize
    return None
    
def get_localFileSize(fname):
    filestat=os.stat(fname)
    return filestat.st_size

#单一文件下载
def get_object_to_file(bucket, object, filename, headers=None):
    (fpath,fname) = os.path.split(filename)
    if not os.path.exists(fpath):os.makedirs(fpath)
    
    res = get_oss().get_object(bucket, object, headers)
    
    if res.status == 200 or res.status == 206:
        f = file(filename, 'ab')
        data = ''
        while True:
            data = res.read(get_oss().GetBufferSize)
            if len(data) != 0:
                f.write(data)
            else:
                break
        f.close()
        # TODO: get object with flow
    return res
        
#单一文件上传
def put_object(bucket, object, local_file, retry_times=5):
    show_bar = False
    for i in range(retry_times):
        try:
            oss = get_oss(show_bar)
            object = smart_code(object)
            #local_file = smart_code(local_file)
            local_file_size = os.path.getsize(local_file)
            if local_file_size > 10*1024*1024:
                upload_id = ""
                thread_num = 5
                max_part_num = 10000
                headers = {}
                res = oss.multi_upload_file(bucket, object, local_file, upload_id, thread_num, max_part_num, headers)
            else:
                res = oss.put_object_from_file(bucket, object, local_file)
            if 200 == res.status:
                return (True, "upload file %s OK." %(format_utf8(local_file)))
            else:
                return (False, "upload %s FAIL, status:%s, request-id:%s" % (local_file, res.status, res.getheader("x-oss-request-id")))
        except:
            return (False, "put %s/%s from %s exception" % (bucket, object, format_utf8(local_file)))
    return (False,"Unknow error")
 
#PyGTK Window
class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()
        self.connect("destroy", gtk.main_quit)
        
        self.set_size_request(890, 650)
        self.set_title("OSS File Tools  [VNSOFT %s]" %(OSSFT_VERSION))
        self.set_icon_from_file("ico.png")
        self.set_position(gtk.WIN_POS_CENTER)
        
        self.table = gtk.Table(19, 7, True)
        self.table.set_col_spacings(5)
        self.table.set_row_spacings(5)
        self.table.attach(gtk.Label("ACCESS ID:"), 0, 1, 0, 1)
        self.accessid = gtk.Entry()
        self.table.attach(self.accessid , 1, 3, 0, 1)
        self.table.attach(gtk.Label("SECRET ACCESS KEY:"), 3, 4, 0, 1)
        self.accesskey=gtk.Entry()
        self.table.attach(self.accesskey, 4,6, 0, 1)
        bt1=gtk.Button("LOGIN")
        bt1.connect("clicked", self.loginOSS)
        self.table.attach(bt1, 6, 7, 0, 1)
        
        self.createBucketArea()
        self.createLocalArea()
        self.createOutputArea()
        
        self.add(self.table)
        self.show_all()
        
    
    #建立窗口的Bucket区域
    def createBucketArea(self):
        #显示Buckets控件
        self.buckets = gtk.TreeView(gtk.ListStore(str))
        self.buckets.append_column(gtk.TreeViewColumn("Buckets",gtk.CellRendererText(),text=0))
        self.buckets.connect("row-activated",self.selbucket)
        self.table.attach(self.buckets,0,1,1,7)
        #显示OSS文件列表控件
        scrolledwindow = gtk.ScrolledWindow()
        vbox = gtk.VBox()
        vbox.pack_start(scrolledwindow, True)
        model=gtk.ListStore(bool,str,str,str,str,str)
        self.ossfiles = gtk.TreeView(model)
        self.ossfiles.connect("row-activated",self.selBucketDirectory)
        cell = gtk.CellRendererToggle()
        cell.connect("toggled", self.on_toggle, model)
        col = gtk.TreeViewColumn(" ", cell, active=0)
        self.ossfiles.append_column(col)
        colname = gtk.TreeViewColumn("OSS File Name",gtk.CellRendererText(),text=1)
        colname.set_resizable(True)
        self.ossfiles.append_column(colname)
        self.ossfiles.append_column(gtk.TreeViewColumn("Type",gtk.CellRendererText(),text=2))
        self.ossfiles.append_column(gtk.TreeViewColumn("File Size",gtk.CellRendererText(),text=3))
        self.ossfiles.append_column(gtk.TreeViewColumn("Last Modify",gtk.CellRendererText(),text=4))
        self.ossfiles.append_column(gtk.TreeViewColumn("ETag",gtk.CellRendererText(),text=5))
        scrolledwindow.add(self.ossfiles)
        self.table.attach(vbox,1,6,1,7)
        #全选按钮
        self.ossall = gtk.Button("SelectALL")
        self.ossall.connect("clicked",self.OssFilesALL)
        self.ossall.set_sensitive(False)
        self.table.attach(self.ossall,6,7,1,2)
        #下载按钮
        self.btdownload=gtk.Button("Download")
        self.btdownload.connect("clicked",self.download)
        self.btdownload.set_sensitive(False)
        self.table.attach(self.btdownload,6,7,2,3)
        #修改Header按钮
        self.bthead=gtk.Button("Edit Head")
        self.bthead.connect("clicked",self.headEdit)
        self.bthead.set_sensitive(False)
        self.table.attach(self.bthead,6,7,3,4)
        #删除按钮
        self.btdel = gtk.Button("Delete")
        self.btdel.connect("clicked",self.ossDelete)
        self.btdel.set_sensitive(False)
        self.table.attach(self.btdel,6,7,4,5)
        
    #建立窗口的本地文件区域
    def createLocalArea(self):
        #显示本地磁碟控件
        self.localdrive = gtk.TreeView(gtk.ListStore(str))
        self.localdrive.append_column(gtk.TreeViewColumn("Locals",gtk.CellRendererText(),text=0))
        self.localdrive.connect("row-activated",self.selLocalDrive)
        self.table.attach(self.localdrive,0,1,7,13)
        #显示本地文件列表控件
        scrolledwindow = gtk.ScrolledWindow()
        vbox = gtk.VBox()
        vbox.pack_start(scrolledwindow, True)
        model=gtk.ListStore(bool,str,str,str,str)
        self.localfiles=gtk.TreeView(model)
        self.localfiles.connect("row-activated",self.selLocalDirectory)
        cell = gtk.CellRendererToggle()
        cell.connect("toggled", self.on_toggle, model)
        col = gtk.TreeViewColumn(" ", cell, active=0)
        self.localfiles.append_column(col)
        colname=gtk.TreeViewColumn("Local Files",gtk.CellRendererText(),text=1)
        #colname.set_sort_column_id(0)
        colname.set_resizable(True)
        self.localfiles.append_column(colname)
        self.localfiles.append_column(gtk.TreeViewColumn("Type",gtk.CellRendererText(),text=2))
        self.localfiles.append_column(gtk.TreeViewColumn("File Size",gtk.CellRendererText(),text=3))
        self.localfiles.append_column(gtk.TreeViewColumn("Last Modify",gtk.CellRendererText(),text=4))
        scrolledwindow.add(self.localfiles)
        self.table.attach(vbox,1,6,7,13)
        
        #上传按钮
        self.btUpload=gtk.Button("UPload")
        self.btUpload.connect("clicked",self.upLoad)
        self.btUpload.set_sensitive(False)
        self.table.attach(self.btUpload,6,7,8,9)
        #全选按钮
        self.lbtall=gtk.Button("SelectALL")
        self.lbtall.connect("clicked",self.LocalFilesALL)
        self.lbtall.set_sensitive(False)
        self.table.attach(self.lbtall,6,7,7,8)
        #关于
        btabout=gtk.Button("About")
        btabout.connect("clicked",self.about)
        self.table.attach(btabout,6,7,12,13)
    
    #建立窗口中的日志输入区域
    def createOutputArea(self):
        scrolledwindow = gtk.ScrolledWindow()
        vbox = gtk.VBox()
        vbox.pack_start(scrolledwindow, True)
        #日志输出控件
        self.outputtv=gtk.TreeView(gtk.ListStore(str,str))
        cell=gtk.CellRendererText()
        treeviewcolumn1=gtk.TreeViewColumn("Time",cell,text=0)
        treeviewcolumn2=gtk.TreeViewColumn("OUTPUT Log",cell,text=1)
        self.outputtv.append_column(treeviewcolumn1)
        self.outputtv.append_column(treeviewcolumn2)
        scrolledwindow.add(self.outputtv)
        self.table.attach(vbox,0,7,13,19)
        
    def loginOSS(self,widget):
        self.setCursor(True)
        #变量初始化
        self.ossfiles.get_model().clear()
        self.buckets.get_model().clear()
        self.localdrive.get_model().clear()
        self.localfiles.get_model().clear()    
        self.lbtSelectALL=False
        self.localCurrentDirectory=None
        self.lbtall.set_label(self.boolean2text(self.lbtSelectALL))
        self.ossSelectALL=False
        self.ossCurrentDirectory=None
        self.ossall.set_label(self.boolean2text(self.ossSelectALL))
        global ACCESS_ID,SECRET_ACCESS_KEY
        ACCESS_ID=self.accessid.get_text()
        SECRET_ACCESS_KEY=self.accesskey.get_text()
       
        res = cmd_getallbucket()    
        if res:
            if (res.status / 100) == 2:
                body = res.read()
                h = GetServiceXml(body)

                for i in h.list():
                    if len(i) == 2:
                        self.buckets.get_model().append([i[0],])
                        self.outputlog('GET Bucket %s ,create by %s' %(i[0],format_datetime(i[1])))
            else:
                self.outputlog(ErrorXml(res.read()).msg)
        else:
            self.outputlog('Network Connect Error')
            
        #加载本地磁碟
        for d in getDrives():
            self.localdrive.get_model().append([d,])
        
        self.setCursor(False)
    #鼠标双击选择Bucket方法
    def selbucket(self, treeview, path, column):
        model = treeview.get_model()
        tree_iter = model.get_iter(path)
        val=model.get_value(tree_iter, 0)
        self.outputlog('Selected Bucket: %s' %(val))
        self.listBucketFiles(val,None)
        self.ossall.set_sensitive(True)
        self.btdownload.set_sensitive(True)
        self.bthead.set_sensitive(True)
        self.btdel.set_sensitive(True)
        self.setCursor(False)
    #选择当前文件清单中的子目录或文件
    def selBucketDirectory(self,treeview,path,column):
        model = treeview.get_model()
        tree_iter = model.get_iter(path)
        fname=model.get_value(tree_iter,1)
        ftype=model.get_value(tree_iter,2)
        if ftype=='Directory':
            if fname=='..':
               fname=os.path.split(self.ossCurrentDirectory)[0]
               (bucket,object)=parse_bucket_object(fname)
            else:
                (bucket,object)=parse_bucket_object(self.ossCurrentDirectory+'/'+fname)
            self.outputlog('Selected Directory : OSS://%s/%s' %(bucket,object))
            self.listBucketFiles(bucket,object)
            self.setCursor(False)
        else:
            model[tree_iter][0] = not model[tree_iter][0]
            
    #列出Bucket中指定目录的文件清单
    def listBucketFiles(self,bucket,object):
        self.setCursor(True)
        osspath = '/'.join([x for x in [bucket,object] if x])
        (bucket,object)=parse_bucket_object(osspath)
        if object!='':object+='/'
        try:
            res = get_oss().get_bucket(bucket,prefix=object,maxkeys='1000',delimiter='/')
        except:
            self.outputlog('Network Connect Error')
            return
            
        if res.status != 200:
            s=res.read()
            self.outputlog(ErrorXml(s).msg)
            return

        body = res.read()
        self.ossCurrentDirectory = osspath
        hh = GetBucketXml(body)
        (fl, pl) = hh.list()
        model=self.ossfiles.get_model()
        model.clear()
        if object!='':model.append([False,'..','Directory','','',''])
        s=len(format_local(object))
        for j in pl:
            j=format_utf8(format_local(j)[s:])
            model.append([False,j.rstrip('/'),'Directory','','',''])
        for i in fl:
            nf=format_utf8(format_local(i[0])[s:])
            model.append([False,nf,'File',i[3],format_datetime(i[1]),i[2].strip('"')])
    #鼠标双击选择本地硬盘方法
    def selLocalDrive(self,treeview,path,column):
        model = treeview.get_model()
        tree_iter = model.get_iter(path)
        currentlocalpath=model.get_value(tree_iter,0)
        self.outputlog('Selected drive %s' % (currentlocalpath))
        self.ListLocalFiles(currentlocalpath,'')
        self.setCursor(False)
        self.btUpload.set_sensitive(True)
        self.lbtall.set_sensitive(True)
    #选择当前文件清单中的子目录或文件
    def selLocalDirectory(self,treeview,path,column):
        model = treeview.get_model()
        tree_iter = model.get_iter(path)
        fname=model.get_value(tree_iter,1) #名称
        ftype=model.get_value(tree_iter,2) #类型
        if ftype=='Directory':
            if fname=='..':
                fname = os.path.split(self.localCurrentDirectory)[0]
                (path,fname) = os.path.split(fname)
            else:
                path = self.localCurrentDirectory
            self.outputlog('Selected Directory : %s' %(os.path.join(format_utf8(path),format_utf8(fname))))
            self.ListLocalFiles(path,fname)
            self.setCursor(False)
        else:
            model[tree_iter][0] = not model[tree_iter][0]
        
    #列出指定目录下的所有文件清单
    def ListLocalFiles(self, path, fname):
        self.setCursor(True)
        self.localfiles.get_model().clear()
        self.lbtSelectALL=False
        self.lbtall.set_label(self.boolean2text(self.lbtSelectALL))
        result=None
 
        tmppath= os.path.join(path,format_local(fname))
        try:
            if not os.path.isdir(tmppath): return
            result = os.listdir(tmppath)
            self.localCurrentDirectory=tmppath
        except:
            self.outputlog('Read Error for %s' % (tmppath))
        if fname!='':self.localfiles.get_model().append([None,'..','Directory',None,None])
        if not result:return
        for i in result:
           try:
               filestat=os.stat(os.path.join(self.localCurrentDirectory,i))
               mtime=format_LocalModifyTime(filestat.st_mtime)
           except:
               continue
           if stat.S_ISDIR(filestat.st_mode):
               ftype='Directory'
           else:    
               ftype='File'
               
           self.localfiles.get_model().append([False,format_utf8(i),ftype,filestat.st_size,mtime])
    
    #OSS文件列表全选或取消全选
    def OssFilesALL(self,widget):
        self.ossSelectALL = not self.ossSelectALL
        self.set_treeviewALL(self.ossfiles,self.ossSelectALL)
        self.ossall.set_label(self.boolean2text(self.ossSelectALL))
        
    #本地文件列表全选或取消全选
    def LocalFilesALL(self,widget):
        self.lbtSelectALL = not self.lbtSelectALL
        self.set_treeviewALL(self.localfiles,self.lbtSelectALL)
        self.lbtall.set_label(self.boolean2text(self.lbtSelectALL))
    
    #取得OSS上当前选择文件或目录下所有子文件
    def getOssSelectedAllFiles(self,result,localcurrent):
        allfiles=[]
        for fs in result:
            if fs[2]=='File':
                allfiles.append([self.ossCurrentDirectory+'/'+fs[1],os.path.join(localcurrent,format_local(fs[1]))])
            else:
                (bucket, object) = parse_bucket_object(self.ossCurrentDirectory+'/'+fs[1]) 
                res = get_oss().get_bucket(bucket,prefix=object+'/',maxkeys='1000')
                if res.status != 200:
                    s=res.read()
                    self.outputlog(ErrorXml(s).msg)
                    continue

                body = res.read()
                hh = GetBucketXml(body)
                (fl, pl) = hh.list()
                for f in fl:
                    fp=os.path.join(localcurrent,os.path.split(format_local(object))[-1])
                    allfiles.append([bucket+'/'+f[0],os.path.join(fp,format_local(f[0])[len(format_local(object+'/')):])])
        return allfiles
    
    def ossDelete(self,widget):
        result=self.get_treeviewSelected(self.ossfiles)
        if result.__len__()==0:
            self.msgbox("Please Select OSS files.")
            return
        allfiles= self.getOssSelectedAllFiles(result,'')
        object_list=[]
        for fn in allfiles:
            (bucket, object) = parse_bucket_object(fn[0]) 
            object_list.append(object)
        delete_result = get_oss().batch_delete_objects(bucket, object_list)
        if delete_result:
            self.outputlog("Selected Files is Deleted.")
            self.listBucketFiles(self.ossCurrentDirectory,None)
        else:
            self.outputlog("Selected Files Delete failed.")
    #下载文件
    def download(self,widget):
        self.setCursor(True)
        responseID=None
        result=self.get_treeviewSelected(self.ossfiles)
        if result.__len__()==0:
            self.msgbox("Please Select OSS files.")
            return
        if not self.localCurrentDirectory:
            self.msgbox("Please Select local path.")
            return
        allfiles= self.getOssSelectedAllFiles(result,self.localCurrentDirectory)
 
        for fn in allfiles:
            (bucket, object) = parse_bucket_object(fn[0]) 
            header={}
            if os.path.exists(fn[1]):
                if responseID==None:
                    (response,chkbut)=self.filemsgbox('filename:%s is exists,\nAre you want?' % (format_utf8(fn[1])))
                    if chkbut:responseID=response
                else:
                    response = responseID
                if response <0:return #关闭对话框时返回，不再执行下载动作
                if response==0:os.remove(fn[1]) #选择覆盖文件时，先删除旧文件
                if response==1:continue #选择忽略文件时，跳过当前文件操作
                if response==2:
                    ossfilesize = get_ossFileSize(bucket, object)
                    localsize = get_localFileSize(fn[1])
                    if localsize>ossfilesize:
                        os.remove(fn[1])
                    else:
                        header={'Range':'bytes=%s-%s'%(localsize.__str__(),(int(ossfilesize)-1).__str__())}
                
            res = get_object_to_file(bucket, object, fn[1],header)
            if res.status == 200 or res.status == 206:
                self.outputlog("The object %s is downloaded to %s, please check." % (object, format_utf8(fn[1])))
            else:
                s=res.read()
                self.outputlog(ErrorXml(s).msg)      
       
        self.ListLocalFiles(self.localCurrentDirectory,'')
        self.setCursor(False)
    #上传文件
    def upLoad(self,widget):
        self.setCursor(True)
        result=self.get_treeviewSelected(self.localfiles)
        if result.__len__()==0:
            self.msgbox("Please Select local files.")
            return
        if not self.ossCurrentDirectory:
            self.msgbox("Please Select Bucket .")
            return
        for fs in result:
            if fs[2]=='File':
                (flag,msg)=put_object(self.ossCurrentDirectory,fs[1],os.path.join(self.localCurrentDirectory,format_local(fs[1])))
                self.outputlog(msg)
            else:
                fn=format_local(fs[1])
                #如果选定的是文件夹，则上传文件夹下所有内容
                for root, dirs, files in os.walk(os.path.join(self.localCurrentDirectory,fn), True):
                    for subfile in files:
                        osspath = format_utf8(os.path.join(root,subfile).strip(self.localCurrentDirectory).replace('\\','/'))
                        (flag,msg)=put_object(self.ossCurrentDirectory,osspath,os.path.join(root,subfile))
                        self.outputlog(msg)
            
        self.listBucketFiles(self.ossCurrentDirectory,None)
        self.setCursor(False)
    #修改文件的Head
    def headEdit(self,widget):
        result=self.get_treeviewSelected(self.ossfiles)
        if result.__len__()==0:
            self.msgbox("Please Select OSS files.")
            return
        headresult = self.headEditBox()
        if headresult.__len__()==0:
            self.msgbox("Please Select Head Keys.")
            return
        headers={}
        for h in headresult:
            headers[h[1]] = h[2]
        allfiles= self.getOssSelectedAllFiles(result,'')
        for fn in allfiles:
            new_header = {}
            for i,k in headers.items():
                new_header[i] = k
            (bucket, object) = parse_bucket_object(fn[0]) 
            #取得目前oss上文件的header
            res = get_oss().get_object(bucket,object)
            if res.status==200:
                header = {}
                header = convert_header2map(res.getheaders())
                for i,k in header.items():
                    if [x.lower() for x in HEADER_KEYS].count(i.lower())==0:continue
                    if not new_header.has_key(i.lower()):#如果新增的内容不包含原来的key,则还是将原来的key加上
                        new_header[i] = k
            
            res = get_oss().copy_object(bucket, object, bucket, object, new_header)
            if res.status == 200:
                self.outputlog("Object Header Edit OK: oss://%s/%s" % (bucket, object))
            else:
                s=res.read()
                self.outputlog("Object Header Edit Error: oss://%s/%s, %s" % (bucket, object,ErrorXml(s).msg)     )         
        
    #全选按钮文字转换
    def boolean2text(self,bvalue):
        if bvalue:
            return 'DeSelectALL'
        else:
            return 'SelectALL'
            
    #文件列表中选择栏位的切换方法    
    def on_toggle(self,cell, path, model, *ignore):
        if path is not None:
            it = model.get_iter(path)
            if model[it][1]=='..':return
            model[it][0] = not model[it][0]
            
    
    def edited_text(self,cell,path, new_text, user_data):
        user_data[path][2] = new_text
        user_data[path][0] = True
        return
      
    #设置TreeView控件中的所有项目全选或取消全选
    def set_treeviewALL(self,tree,bvalue):
        model = tree.get_model()
        treeiter = model.get_iter_first()
        while treeiter:
            if model.get_value(treeiter,1)!='..':
                model.set_value(treeiter,0,bvalue)
            treeiter = model.iter_next(treeiter)
    
    #返回TreeView控件中已经选择的项目内容
    def get_treeviewSelected(self,tree):
        result=[]
        model=tree.get_model()
        treeiter = model.get_iter_first()
        while treeiter:
            #判断是否有进行选择
            if model.get_value(treeiter,0):
                rowiter=[]
                #把选中行的各栏位的值转换为列表中
                for i in model[treeiter]:
                    rowiter.append(i)
                result.append(rowiter)
            treeiter = model.iter_next(treeiter)        
        return result
    
    #公用提示信息窗口
    def msgbox(self,msgstr):
        dialog = gtk.MessageDialog(self,0,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,msgstr)    
        dialog.run()
        dialog.destroy()
    
    #修改head时弹出的对话框
    def headEditBox(self):
        model=gtk.ListStore(bool,str,str)
        treeview =  gtk.TreeView(model)
        
        cell = gtk.CellRendererToggle()
        cell.connect("toggled", self.on_toggle, model,(2,'editable'))
        col = gtk.TreeViewColumn(" ", cell, active=0)
        treeview.append_column(col)
        
        treeview.append_column(gtk.TreeViewColumn("Head Key",gtk.CellRendererText(),text=1))
        cellt=gtk.CellRendererText()
        cellt.set_property("editable", "True")
        cellt.set_property("editable-set", "True")
        cellt.connect('edited', self.edited_text,model)
        colv=gtk.TreeViewColumn("Head Value",cellt,text=2,width=300)
        
        treeview.append_column(colv)
        for i in HEADER_KEYS:
            model.append([False,i,''])
        dialog = gtk.Dialog("Head Edit dialog",None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
        dialog.set_position(gtk.WIN_POS_CENTER)
        dialog.set_size_request(400, 200)
        dialog.vbox.pack_start(treeview)
        treeview.show()
        response = dialog.run()
        result=[]
        if response ==  gtk.RESPONSE_ACCEPT:
           result = self.get_treeviewSelected(treeview)
           
        dialog.destroy()   
        return result
        
    #下载文件存在时，弹出对话框，可确认覆盖、忽略、续传动作
    def filemsgbox(self,msgstr):
        label = gtk.Label(msgstr)
        dialog = gtk.Dialog("File dialog",None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
        dialog.set_position(gtk.WIN_POS_CENTER)
        dialog.add_button("OverWrite",0)
        dialog.add_button("Ignore",1)
        dialog.add_button("Resume",2)
        dialog.vbox.pack_start(label)
        label.show()
        checkbox = gtk.CheckButton("Remember my choice?")
        dialog.action_area.pack_start(checkbox)
        checkbox.show()
        response = dialog.run()
        dialog.destroy()
        return (response,checkbox.get_active())
        
    #日志输入公用方法    
    def outputlog(self,msg):
         self.outputtv.get_model().prepend([datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),msg,])    
    
    def setCursor(self,cursorType):
        if cursorType:
            cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
        else:
            cursor = gtk.gdk.Cursor(gtk.gdk.ARROW)
        self.get_root_window().set_cursor(cursor)    
    #构建关于弹出窗口     
    def about(self, widget):
        abd = gtk.AboutDialog()
        abd.set_position(gtk.WIN_POS_CENTER)
        abd.set_program_name("OSS File Tools")
        abd.set_version(OSSFT_VERSION)
        abd.set_copyright("(c) VnSoft,vnsoft.he@gmail.com")
        abd.set_comments("File Manager Tools for aliyun's OSS")
        abd.set_website("http://git.oschina.net/vnsoft/OSS-File-Tools/")
        abd.set_logo(gtk.gdk.pixbuf_new_from_file("ico.png"))
        abd.run()
        abd.destroy()

#主程序执行        
if __name__=="__main__":
    PyApp()
    gtk.main()
